﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using WeMakeSP.ImageResizer.Configuration;
using WeMakeSP.ImageResizer.Services.ServiceContract;
using WeMakeSP.ImageResizer.Services.ServiceImplementation;
using WeMakeSP.Platform.SharePoint.Lists;

namespace WeMakeSP.ImageResizer
{
    /// <summary>
    /// This job apply resizing policies
    /// to a specific picture library.
    /// </summary>
    public class PictureLibraryResizerJob : SPJobDefinition
    {
        #region Constants

        /// <summary>
        /// The job name prefix.
        /// </summary>
        public const string Prefix = "Image Resizer ";

        #endregion

        #region Fields

        /// <summary>
        /// The configuration.
        /// </summary>
        private ImageResizerPictureLibraryConfiguration config;

        /// <summary>
        /// The list id.
        /// </summary>
        [Persisted]
        private Guid listId;

        /// <summary>
        /// The site id.
        /// </summary>
        [Persisted]
        private Guid siteId;

        /// <summary>
        /// The web id.
        /// </summary>
        [Persisted]
        private Guid webId;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PictureLibraryResizerJob"/> class.
        /// </summary>
        public PictureLibraryResizerJob()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PictureLibraryResizerJob"/> class.
        /// </summary>
        /// <param name="webApp">The web app.</param>
        /// <param name="config">The config.</param>
        public PictureLibraryResizerJob(SPWebApplication webApp, ImageResizerPictureLibraryConfiguration config) :
            base(null, webApp, null, SPJobLockType.None)
        {
            this.config = config;
        }

        #endregion

        #region Public Properties

        #endregion

        #region Public Methods

        /// <summary>
        /// Executes the job definition.
        /// </summary>
        /// <param name="targetInstanceId">For target types of <see cref="T:Microsoft.SharePoint.Administration.SPContentDatabase"></see> this is the database ID of the content database being processed by the running job. This value is Guid.Empty for all other target types.</param>
        public override void Execute(Guid targetInstanceId)
        {
            using (SPSite site = new SPSite(this.siteId))
            {
                using (SPWeb web = site.OpenWeb(this.webId))
                {
                    // Enable unsafe updates.

                    web.AllowUnsafeUpdates = true;

                    // Get a reference to the picture library.

                    SPList pictureLibrary =
                        web.Lists.GetSafeListById(this.listId);

                    // The picture library was found.

                    if (pictureLibrary != null)
                    {
                        IImageResizerService imgResizerservice =
                            new ImageResizerService(web);

                        ImageResizerPictureLibraryConfiguration configuration =
                            imgResizerservice.GetPictureLibraryConfiguration(this.listId);

                        this.ProcessPictureLibrary(web, pictureLibrary, configuration);
                    }

                    // Disable unsafe updates.

                    web.AllowUnsafeUpdates = false;
                }
            }
        }
       
        /// <summary>
        /// Sets the picture library.
        /// </summary>
        /// <param name="siteId">The site id.</param>
        /// <param name="webId">The web id.</param>
        /// <param name="pictureLibraryId">The picture library id.</param>
        public void SetPictureLibrary(Guid siteId, Guid webId, Guid pictureLibraryId)
        {
            this.listId = pictureLibraryId;
            this.siteId = siteId;
            this.webId = webId;
        }

        /// <summary>
        /// Submits the job.
        /// </summary>
        public void SubmitJob()
        {
            this.SubmitJob(
                this.config.Schedule, 
                this.config.HourSchedule, 
                this.config.MinuteSchedule);
        }

        /// <summary>
        /// Submits the job.
        /// </summary>
        /// <param name="scheduleType">Type of the schedule.</param>
        public void SubmitJob(ImageResizerSchedule scheduleType)
        {
            this.SubmitJob(scheduleType, DateTime.Now.Hour, DateTime.Now.Minute);
        }

        /// <summary>
        /// Submits the job.
        /// </summary>
        /// <param name="scheduleType">Type of the schedule.</param>
        /// <param name="hours">The hours.</param>
        /// <param name="minutes">The minutes.</param>
        public void SubmitJob(ImageResizerSchedule scheduleType, int hours, int minutes)
        {
            DateTime jobTime =
                new DateTime(
                    DateTime.Now.Year,
                    DateTime.Now.Month,
                    DateTime.Now.Day,
                    hours,
                    minutes,
                    0);

            this.SubmitJobInternal(scheduleType, jobTime);
        }

        #endregion

        #region Public Static Method

        /// <summary>
        /// Gets the job token.
        /// </summary>
        /// <param name="listId">The list id.</param>
        /// <returns></returns>
        public static string GetJobToken(Guid listId)
        {
            StringBuilder titleBuilder =
               new StringBuilder();

            titleBuilder.Append("Image Resizer ");
            titleBuilder.Append("ListId:");
            titleBuilder.Append(listId);

            return 
                titleBuilder.ToString();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Processes the picture library.
        /// </summary>
        /// <param name="web">The SharePoint web.</param>
        /// <param name="pictureLibrary">The picture library.</param>
        /// <param name="configuration">The configuration.</param>
        private void ProcessPictureLibrary(SPWeb web, SPList pictureLibrary, ImageResizerPictureLibraryConfiguration configuration)
        {
            // Prepare the CAML query.

            string view = "Scope=\"RecursiveAll\" ModerationType=\"Moderator\"";

            string where = "<Where><Or><Gt><FieldRef Name='ImageWidth' /><Value Type='Integer'>{0}</Value></Gt><Gt><FieldRef Name='ImageHeight' /><Value Type='Integer'>{1}</Value></Gt></Or></Where>";

            where =
                string.Format(
                    where,
                    configuration.MaxWidth,
                    configuration.MaxHeight);

            SPQuery query =
                new SPQuery()
                {
                    Query = where,
                    ViewAttributes = view
                };

            // Get a list of oversized pictures.

            SPListItemCollection overSizedPictures =
               pictureLibrary.GetItems(query);

            foreach (SPListItem picture in overSizedPictures)
            {
                ImageUploadPolicy imageUploadPolicy =
                   new ImageUploadPolicy(web, picture);

                // Apply the policies to the item.

                imageUploadPolicy.ApplyPolicies();
            }
        }

        /// <summary>
        /// Submits the job internal.
        /// </summary>
        /// <param name="scheduleType">Type of the schedule.</param>
        /// <param name="jobStart">The job start.</param>
        private void SubmitJobInternal(ImageResizerSchedule scheduleType, DateTime jobStart)
        {            
            switch (this.config.Schedule)
            {
                case ImageResizerSchedule.Immediate:

                    throw
                        new NotSupportedException(this.config.Schedule.ToString());
                case ImageResizerSchedule.Daily:

                    SPDailySchedule dailySchedule =
                        new SPDailySchedule();

                    dailySchedule.BeginHour = jobStart.Hour;
                    dailySchedule.BeginMinute = jobStart.Minute;
                    dailySchedule.EndHour = jobStart.Hour;
                    dailySchedule.EndMinute = jobStart.Minute;

                    this.Schedule = dailySchedule;

                    break;
                case ImageResizerSchedule.Weekly:
                    
                    SPWeeklySchedule weeklySchedule =
                        new SPWeeklySchedule();

                    weeklySchedule.BeginHour = jobStart.Hour;
                    weeklySchedule.BeginMinute = jobStart.Minute;
                    weeklySchedule.EndHour = jobStart.Hour;
                    weeklySchedule.EndMinute = jobStart.Minute;

                    this.Schedule = weeklySchedule;

                    break;
                case ImageResizerSchedule.Monthly:

                    SPMonthlySchedule monthlySchedule =
                        new SPMonthlySchedule();

                    monthlySchedule.BeginHour = jobStart.Hour;
                    monthlySchedule.BeginMinute = jobStart.Minute;
                    monthlySchedule.EndHour = jobStart.Hour;
                    monthlySchedule.EndMinute = jobStart.Minute;

                    this.Schedule = monthlySchedule;

                    break;
                default:
                    throw new NotSupportedException("ImageResizerSchedule");
            }
            
            this.Retry = false;
            this.Title = GetJobToken(listId);
            this.Name = GetJobToken(listId);
            this.Update();
        }

        #endregion
    }
}
